import {
  ColumnCellRenderParams,
  ColumnEditRenderOptions,
  ColumnEditRenderParams,
  ColumnExportCellRenderParams,
  ColumnFilterMethodParams,
  ColumnFilterParams,
  ColumnFilterRenderOptions,
  ColumnFilterRenderParams,
  FormItemRenderOptions,
  FormItemRenderParams,
  InterceptorParams,
  RenderOptions,
  RenderParams,
  TableRenderParams
} from "vxe-table";
import XEUtils from "xe-utils";
import Vue, {CreateElement} from "vue";
import {MyColumnCellRenderParams, MyColumnEditRenderParams} from "./MyTables";

export function isEmptyValue(cellValue: any) {
  return cellValue === null || cellValue === undefined || cellValue === ''
}

export function getModelProp(renderOpts: RenderOptions) {
  return 'value'
}

export function getModelEvent(renderOpts: RenderOptions) {
  return 'input'
}

export function getChangeEvent(renderOpts: RenderOptions) {
  let type = 'change'
  switch (renderOpts.name) {
    case 'ElAutocomplete':
      type = 'select'
      break
    case 'ElInput':
    case 'ElInputNumber':
      type = 'input'
      break
  }
  return type
}

export function parseDate(value: any, props: { [key: string]: any }) {
  return value && props.valueFormat ? XEUtils.toStringDate(value, props.valueFormat) : value
}

export function getFormatDate(value: any, props: { [key: string]: any }, defaultFormat: string) {
  return XEUtils.toDateString(parseDate(value, props), props.format || defaultFormat)
}

export function getFormatDates(values: any[], props: { [key: string]: any }, separator: string, defaultFormat: string) {
  return XEUtils.map(values, (date: any) => getFormatDate(date, props, defaultFormat)).join(separator)
}

export function equalDaterange(cellValue: any, data: any, props: { [key: string]: any }, defaultFormat: string) {
  cellValue = getFormatDate(cellValue, props, defaultFormat)
  return cellValue >= getFormatDate(data[0], props, defaultFormat) && cellValue <= getFormatDate(data[1], props, defaultFormat)
}

export function getCellEditFilterProps(renderOpts: RenderOptions, params: TableRenderParams, value: any, defaultProps?: { [prop: string]: any }) {
  const {vSize, editConfig} = params.$table
  let edit = true
  if (editConfig) {
    // @ts-ignore
    edit = editConfig.enabled
    if (edit) {
      const paramsBak = params as MyColumnCellRenderParams | MyColumnEditRenderParams
      if (false === editConfig.beforeEditMethod?.({
        row: paramsBak.row,
        rowIndex: paramsBak.rowIndex,
        column: paramsBak.column,
        columnIndex: paramsBak.columnIndex
      })) {
        edit = false
      }
    }
  }
  if (defaultProps) {
    defaultProps.size = vSize
    defaultProps.disabled = !edit
  } else {
    defaultProps = {
      size: vSize,
      disabled: !edit
    }
  }
  return XEUtils.assign({}, defaultProps, renderOpts.props, {[getModelProp(renderOpts)]: value})
}

export function getItemProps(renderOpts: RenderOptions, params: FormItemRenderParams, value: any, defaultProps?: { [prop: string]: any }) {
  const {vSize} = params.$form
  return XEUtils.assign(vSize ? {size: vSize} : {}, defaultProps, renderOpts.props, {[getModelProp(renderOpts)]: value})
}

export function formatText(cellValue: any) {
  return '' + (isEmptyValue(cellValue) ? '' : cellValue)
}

export function getCellLabelVNs(h: CreateElement, renderOpts: ColumnEditRenderOptions, params: ColumnEditRenderParams, cellLabel: any) {
  const {placeholder} = renderOpts
  return [
    h('span', {
      class: 'vxe-cell--label'
    }, placeholder && isEmptyValue(cellLabel)
      ? [
        h('span', {
          class: 'vxe-cell--placeholder'
        }, formatText(placeholder))
      ]
      : formatText(cellLabel))
  ]
}

export function getNativeOns(renderOpts: RenderOptions, params: RenderParams) {
  const {nativeEvents} = renderOpts
  const nativeOns: { [type: string]: Function } = {}
  XEUtils.objectEach(nativeEvents, (func: Function, key: string) => {
    nativeOns[key] = function (...args: any[]) {
      func(params, ...args)
    }
  })
  return nativeOns
}

export function getOns(renderOpts: RenderOptions, params: RenderParams, inputFunc?: Function, changeFunc?: Function) {
  const {events} = renderOpts
  const modelEvent = getModelEvent(renderOpts)
  const changeEvent = getChangeEvent(renderOpts)
  const isSameEvent = changeEvent === modelEvent
  const ons: { [type: string]: Function } = {}
  XEUtils.objectEach(events, (func: Function, key: string) => {
    ons[key] = function (...args: any[]) {
      func(params, ...args)
    }
  })
  if (inputFunc) {
    ons[modelEvent] = function (targetEvnt: any) {
      inputFunc(targetEvnt)
      if (events && events[modelEvent]) {
        events[modelEvent](params, targetEvnt)
      }
      if (isSameEvent && changeFunc) {
        changeFunc(targetEvnt)
      }
    }
  }
  if (!isSameEvent && changeFunc) {
    ons[changeEvent] = function (...args: any[]) {
      changeFunc(...args)
      if (events && events[changeEvent]) {
        events[changeEvent](params, ...args)
      }
    }
  }
  return ons
}

export function getEditOns(renderOpts: RenderOptions, params: ColumnEditRenderParams) {
  const {$table, row, column} = params
  return getOns(renderOpts, params, (value: any) => {
    // 处理 model 值双向绑定
    Vue.set(row, column.field, value)
  }, () => {
    // 处理 change 事件相关逻辑
    $table.updateStatus(params)
  })
}

export function getFilterOns(renderOpts: RenderOptions, params: ColumnFilterRenderParams, option: ColumnFilterParams, changeFunc: Function) {
  return getOns(renderOpts, params, (value: any) => {
    // 处理 model 值双向绑定
    Vue.set(option, 'data', value)
  }, changeFunc)
}

export function getItemOns(renderOpts: RenderOptions, params: FormItemRenderParams) {
  const {$form, data, property} = params
  return getOns(renderOpts, params, (value: any) => {
    // 处理 model 值双向绑定
    Vue.set(data, property, value)
  }, () => {
    // 处理 change 事件相关逻辑
    $form.updateStatus(params)
  })
}

export function matchCascaderData(index: number, list: any[], values: any[], labels: any[]) {
  const val = values[index]
  if (list && values.length > index) {
    XEUtils.each(list, (item) => {
      if (item.value === val) {
        labels.push(item.label)
        matchCascaderData(++index, item.children, values, labels)
      }
    })
  }
}

export function getCascaderCellValue(renderOpts: RenderOptions, params: ColumnCellRenderParams) {
  const {props = {}} = renderOpts
  const {row, column} = params
  const cellValue = XEUtils.get(row, column.property)
  const values: any[] = cellValue || []
  const labels: any[] = []
  matchCascaderData(0, props.options, values, labels)
  return (props.showAllLevels === false ? labels.slice(labels.length - 1, labels.length) : labels).join(` ${props.separator || '/'} `)
}

export function getDatePickerCellValue(renderOpts: RenderOptions, params: ColumnCellRenderParams | ColumnExportCellRenderParams) {
  const {props = {}} = renderOpts
  const {row, column} = params
  const {rangeSeparator = '-'} = props
  let cellValue = XEUtils.get(row, column.property)
  switch (props.type) {
    case 'week':
      cellValue = getFormatDate(cellValue, props, 'yyyywWW')
      break
    case 'month':
      cellValue = getFormatDate(cellValue, props, 'yyyy-MM')
      break
    case 'year':
      cellValue = getFormatDate(cellValue, props, 'yyyy')
      break
    case 'dates':
      cellValue = getFormatDates(cellValue, props, ', ', 'yyyy-MM-dd')
      break
    case 'daterange':
      cellValue = getFormatDates(cellValue, props, ` ${rangeSeparator} `, 'yyyy-MM-dd')
      break
    case 'datetimerange':
      cellValue = getFormatDates(cellValue, props, ` ${rangeSeparator} `, 'yyyy-MM-dd HH:ss:mm')
      break
    case 'monthrange':
      cellValue = getFormatDates(cellValue, props, ` ${rangeSeparator} `, 'yyyy-MM')
      break
    default:
      cellValue = getFormatDate(cellValue, props, 'yyyy-MM-dd')
  }
  return cellValue
}

export function getTimePickerCellValue(renderOpts: RenderOptions, params: ColumnCellRenderParams | ColumnEditRenderParams) {
  const {props = {}} = renderOpts
  const {row, column} = params
  const {isRange, format = 'hh:mm:ss', rangeSeparator = '-'} = props
  let cellValue = XEUtils.get(row, column.property)
  if (cellValue && isRange) {
    cellValue = XEUtils.map(cellValue, (date) => XEUtils.toDateString(parseDate(date, props), format)).join(` ${rangeSeparator} `)
  }
  return XEUtils.toDateString(parseDate(cellValue, props), format)
}

export function createEditRender(defaultProps?: { [key: string]: any }) {
  return function (h: CreateElement, renderOpts: ColumnEditRenderOptions, params: ColumnEditRenderParams) {
    const {row, column} = params
    const {attrs} = renderOpts
    const cellValue = XEUtils.get(row, column.property)
    return [
      h(renderOpts.name, {
        attrs: {
          ...renderOpts.props,
          ...attrs,
        },
        props: getCellEditFilterProps(renderOpts, params, cellValue, defaultProps),
        on: getEditOns(renderOpts, params),
        nativeOn: getNativeOns(renderOpts, params)
      })
    ]
  }
}

export function defaultButtonEditRender(h: CreateElement, renderOpts: ColumnEditRenderOptions, params: ColumnEditRenderParams) {
  const {attrs} = renderOpts
  return [
    h('el-button', {
      attrs,
      props: getCellEditFilterProps(renderOpts, params, null),
      on: getOns(renderOpts, params),
      nativeOn: getNativeOns(renderOpts, params)
    }, cellText(h, renderOpts.content))
  ]
}

export function defaultButtonsEditRender(h: CreateElement, renderOpts: ColumnEditRenderOptions, params: ColumnEditRenderParams) {
  return renderOpts.children.map((childRenderOpts: ColumnEditRenderOptions) => defaultButtonEditRender(h, childRenderOpts, params)[0])
}

export function createFilterRender(defaultProps?: { [key: string]: any }) {
  return function (h: CreateElement, renderOpts: ColumnFilterRenderOptions, params: ColumnFilterRenderParams) {
    const {column} = params
    const {name, attrs} = renderOpts
    const nativeOn = getNativeOns(renderOpts, params)
    return [
      h('div', {
        class: 'vxe-table--filter-element-wrapper'
      }, column.filters.map((option, oIndex) => {
        const optionValue = option.data
        return h(name, {
          key: oIndex,
          attrs,
          props: getCellEditFilterProps(renderOpts, params, optionValue, defaultProps),
          on: getFilterOns(renderOpts, params, option, () => {
            // 处理 change 事件相关逻辑
            handleConfirmFilter(params, !!option.data, option)
          }),
          nativeOn
        })
      }))
    ]
  }
}

export function handleConfirmFilter(params: ColumnFilterRenderParams, checked: boolean, option: ColumnFilterParams) {
  const {$panel} = params
  $panel.changeOption({}, checked, option)
}

/**
 * 模糊匹配
 * @param params
 */
export function defaultFuzzyFilterMethod(params: ColumnFilterMethodParams) {
  const {option, row, column} = params
  const {data} = option
  const cellValue = XEUtils.get(row, column.property)
  return XEUtils.toValueString(cellValue).indexOf(data) > -1
}

/**
 * 精确匹配
 * @param params
 */
export function defaultExactFilterMethod(params: ColumnFilterMethodParams) {
  const {option, row, column} = params
  const {data} = option
  const cellValue = XEUtils.get(row, column.property)
  /* eslint-disable eqeqeq */
  return cellValue === data
}

export function cellText(h: CreateElement, cellValue: any) {
  return [formatText(cellValue)]
}

export function createFormItemRender(defaultProps?: { [key: string]: any }) {
  return function (h: CreateElement, renderOpts: FormItemRenderOptions, params: FormItemRenderParams) {
    const {data, property} = params
    const {name} = renderOpts
    const {attrs} = renderOpts
    const itemValue = XEUtils.get(data, property)
    return [
      h(name, {
        attrs,
        props: getItemProps(renderOpts, params, itemValue, defaultProps),
        on: getItemOns(renderOpts, params),
        nativeOn: getNativeOns(renderOpts, params)
      })
    ]
  }
}

export function defaultButtonItemRender(h: CreateElement, renderOpts: FormItemRenderOptions, params: FormItemRenderParams) {
  const {attrs} = renderOpts
  const props = getItemProps(renderOpts, params, null)
  return [
    h('el-button', {
      attrs,
      props,
      on: getOns(renderOpts, params),
      nativeOn: getNativeOns(renderOpts, params)
    }, cellText(h, renderOpts.content || props.content))
  ]
}

export function defaultButtonsItemRender(h: CreateElement, renderOpts: FormItemRenderOptions, params: FormItemRenderParams) {
  return renderOpts.children.map((childRenderOpts: FormItemRenderOptions) => defaultButtonItemRender(h, childRenderOpts, params)[0])
}

export function createExportMethod(getExportCellValue: Function) {
  return function (params: ColumnExportCellRenderParams) {
    const {row, column, options} = params
    return options && options.original ? XEUtils.get(row, column.property) : getExportCellValue(column.editRender || column.cellRender, params)
  }
}

export function createFormItemRadioAndCheckboxRender() {
  return function (h: CreateElement, renderOpts: FormItemRenderOptions, params: FormItemRenderParams) {
    const {name, options = [], optionProps = {}, attrs} = renderOpts
    const {data, property} = params
    const labelProp = optionProps.label || 'label'
    const valueProp = optionProps.value || 'value'
    const itemValue = XEUtils.get(data, property)
    return [
      h(`${name}Group`, {
        attrs,
        props: getItemProps(renderOpts, params, itemValue),
        on: getItemOns(renderOpts, params),
        nativeOn: getNativeOns(renderOpts, params)
      }, options.map((option, oIndex) => {
        return h(name, {
          key: oIndex,
          props: {
            label: option[valueProp],
            disabled: option.disabled
          }
        }, option[labelProp])
      }))
    ]
  }
}

/**
 * 检查触发源是否属于目标节点
 */
export function getEventTargetNode(evnt: any, container: HTMLElement, className: string) {
  let targetElem
  let target = evnt.target
  while (target && target.nodeType && target !== document) {
    if (className && target.className && target.className.split && target.className.split(' ').indexOf(className) > -1) {
      targetElem = target
    } else if (target === container) {
      return {flag: className ? !!targetElem : true, container, targetElem: targetElem}
    }
    target = target.parentNode
  }
  return {flag: false}
}

/**
 * 事件兼容性处理
 */
export function handleClearEvent(params: InterceptorParams, e: any) {
  const bodyElem = document.body
  const evnt = params.$event || e
  if (
    // 远程搜索
    getEventTargetNode(evnt, bodyElem, 'el-autocomplete-suggestion').flag ||
    // 下拉框
    getEventTargetNode(evnt, bodyElem, 'el-select-dropdown').flag ||
    // 级联
    getEventTargetNode(evnt, bodyElem, 'el-cascader__dropdown').flag ||
    getEventTargetNode(evnt, bodyElem, 'el-cascader-menus').flag ||
    // 日期
    getEventTargetNode(evnt, bodyElem, 'el-time-panel').flag ||
    getEventTargetNode(evnt, bodyElem, 'el-picker-panel').flag ||
    // 颜色
    getEventTargetNode(evnt, bodyElem, 'el-color-dropdown').flag
  ) {
    return false
  }
}
